﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Wicture.EPC.Service.V1.Filters
{
    public class UCString
    {
        protected char _Plus = '&';
        protected char _Minus = '-';
        protected char _Or = '#';
        protected char _LeafOr = ' ';
        protected List<char> ignoreChars = new List<char>();
        private Node TheNode { set; get; }
        public int SaCodeCount { set; get; }
        public UCString()
        {
        }
        public UCString(string bcode)
        {
            BuildBCode(bcode);
        }

        public void SetIgnoreChars(List<char> chars)
        {
            ignoreChars = chars;
        }

        public void BuildBCode(string bcode)
        {
            if (bcode != null)
            {
                foreach(var ignoreChar in ignoreChars)
                {
                    bcode = bcode.Replace(ignoreChar.ToString(), "");
                }
            }
            TheNode = new Node();
            Build(bcode, TheNode);
            SaCodeCount = 0;
        }
        public UCString(string bcode, char plus, char minus, char or)
        {
            SetSympol(plus, minus, or);
            BuildBCode(bcode);
        }
        public UCString(string bcode, char plus, char minus, char or, char leafOr)
        {
            SetSympol(plus, minus, or, leafOr);
            BuildBCode(bcode);
        }
        public UCString(char plus, char minus, char or)
        {
            SetSympol(plus, minus, or);
        }

        public void SetSympol(char plus, char minus, char or)
        {
            _Plus = plus;
            _Minus = minus;
            _Or = or;
        }
        public void SetSympol(char plus, char minus, char or, char leafOr)
        {
            _Plus = plus;
            _Minus = minus;
            _Or = or;
            _LeafOr = leafOr;
        }

        public bool IsMatch(ISet<string> saCodes)
        {
            return IsMatch(saCodes, TheNode); ;
        }

        protected virtual bool MatchLeafNode(ISet<string> saCodes, string leafNode)
        {
            if (_LeafOr == ' ' || !leafNode.Contains(_LeafOr))
            {
                return saCodes.Contains(leafNode);
            }
            else
            {
                return leafNode.Split(_LeafOr).Intersect(saCodes).Any();
            }
        }

        private bool IsMatch(ISet<string> saCodes, Node node)
        {
            if (node.Children == null)
            {
                var flag = MatchLeafNode(saCodes, node.Code);
                if (node.Operator == Operator.PLUS)
                {
                    if (flag) SaCodeCount++;
                    return flag;
                }
                else
                {
                    return !flag;
                }
            }
            else
            {
                var flag = false;
                if (node.ChildrenRelation == Relation.AND)
                {
                    bool isOK = true;
                    foreach (var child in node.Children)
                    {
                        if (!IsMatch(saCodes, child))
                        {
                            isOK = false;
                            break;
                        }
                    }
                    flag = isOK;
                }
                else
                {
                    foreach (var child in node.Children)
                    {
                        if (IsMatch(saCodes, child))
                        {
                            flag = true;
                            break;
                        }
                    }
                }
                if (node.Operator == Operator.PLUS)
                {
                    return flag;
                }
                else
                {
                    return !flag;
                }
            }
        }

        private bool IsMatch2(ISet<string> saCodes, Node node)
        {
            if (node.Children == null)
            {
                var flag = MatchLeafNode(saCodes, node.Code);
                if (node.Operator == Operator.PLUS)
                {
                    if (flag)
                    {
                        node.Level = 1;
                    }
                    node.IsMatch = flag;
                    return flag;
                }
                else
                {
                    node.IsMatch = !flag;
                    return !flag;
                }
            }
            else
            {
                var flag = false;
                if (node.ChildrenRelation == Relation.AND)
                {
                    bool isOK = true;
                    var level = 0;
                    foreach (var child in node.Children)
                    {
                        if (!IsMatch2(saCodes, child))
                        {
                            isOK = false;
                            level = 0;
                            break;
                        }
                        else
                        {
                            level += child.Level;
                        }
                    }
                    node.Level = level;
                    flag = isOK;
                }
                else
                {
                    foreach (var child in node.Children)
                    {
                        if (IsMatch2(saCodes, child))
                        {
                            flag = true;
                            if (node.Level < child.Level)
                                node.Level = child.Level;
                        }
                    }
                }
                if (node.Operator == Operator.PLUS)
                {
                    node.IsMatch = flag;
                    return flag;
                }
                else
                {
                    node.Level = 0;
                    node.IsMatch = !flag;
                    return !flag;
                }
            }
        }

        private void Build(string bcode, Node node)
        {
            bcode = bcode.Trim();
            var childrenCodes = ProcessBrackets(ref bcode).ToArray();
            var orSplits = bcode.Split(new string[] { _Or.ToString() }, StringSplitOptions.RemoveEmptyEntries);
            if (orSplits.Length > 1)
            {
                node.ChildrenRelation = Relation.OR;
                node.Children = new List<Node>();
                var maxLevel = 0;
                foreach (var splitsStr in orSplits)
                {

                    Node newNode = new Node();
                    Build(string.Format(splitsStr, childrenCodes), newNode);
                    if (newNode.Level > maxLevel)
                    {
                        maxLevel = newNode.Level;
                    }
                    node.Children.Add(newNode);
                }
                node.Level += maxLevel;
            }
            else
            {
                var codes = ProcessPulsMinus(orSplits[0]);
                if (codes.Count > 1)
                {
                    node.ChildrenRelation = Relation.AND;
                    node.Children = new List<Node>();
                    foreach (var code in codes)
                    {
                        Node newNode = new Node();
                        Build(string.Format(code, childrenCodes), newNode);
                        node.Level += newNode.Level;
                        node.Children.Add(newNode);
                    }
                }
                else
                {
                    var singleCode = codes[0];
                    if (singleCode.StartsWith(_Minus))
                    {
                        node.Operator = Operator.MINUS;
                    }

                    if (singleCode.Contains('{'))
                    {
                        Build(RomoveBrackets(string.Format(singleCode, childrenCodes)), node);
                    }
                    else
                    {
                        node.Code = singleCode.Substring(1, singleCode.Length - 1).Trim();
                        if (node.Operator == Operator.PLUS)
                        {
                            node.Level = 1;
                        }
                    }
                }
            }
        }

        private string RomoveBrackets(string bcode)
        {
            bcode = bcode.TrimStart(new char[] { _Plus, _Minus }).Trim();
            if (bcode.StartsWith('(') && bcode.EndsWith(')'))
            {
                bcode = bcode.Substring(1, bcode.Length - 2);
            }
            return bcode;
        }

        private List<string> ProcessPulsMinus(string bcode)
        {
            List<string> childCodes = new List<string>();
            if (!bcode.StartsWith(_Plus) && !bcode.StartsWith(_Minus))
            {
                bcode = _Plus + bcode;
            }
            int startPos = 0;
            for (int i = 0; i < bcode.Length; i++)
            {
                if (bcode[i] == _Plus || bcode[i] == _Minus)
                {
                    if (i != 0)
                    {
                        childCodes.Add(bcode.Substring(startPos, i - startPos));
                        startPos = i;
                    }
                }
            }
            childCodes.Add(bcode.Substring(startPos, bcode.Length - startPos));
            return childCodes;
        }

        private List<string> ProcessBrackets(ref string bcode)
        {
            List<string> childCodes = new List<string>();
            while (bcode.IndexOf('(') >= 0)
            {
                int startPos = 0;
                int deep = 0;
                for (int i = 0; i < bcode.Length; i++)
                {
                    if (bcode[i] == '(')
                    {
                        deep++;
                        if (deep == 1)
                        {
                            startPos = i;
                        }
                    }
                    else if (bcode[i] == ')')
                    {
                        deep--;
                        if (deep == 0)
                        {
                            string childCode = bcode.Substring(startPos, i - startPos + 1);
                            bcode = bcode.Substring(0, startPos) + "{" + childCodes.Count + "}" + bcode.Substring(i + 1, bcode.Length - i - 1);
                            childCodes.Add(childCode);

                            break;
                        }
                    }
                }
            }
            return childCodes;
        }

        private class Node
        {
            public bool IsMatch { get; set; }
            public int Level { set; get; }
            public Operator Operator { set; get; }
            public Relation ChildrenRelation { set; get; }
            public string Code { set; get; }
            public List<Node> Children { set; get; }
        }
        private enum Operator
        {
            PLUS,
            MINUS
        }

        private enum Relation
        {
            AND,
            OR
        }
    }
}
